/*****************************************************
** Author:
** Date: 2019-11-13
*
*****************************************************/

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <time.h>
#include <pthread.h>
#include "comm.h"
#include "mplink.h"


#define TEXT_SIZE_MAX	256

struct msgbody {
	unsigned short size;
	char text[TEXT_SIZE_MAX];
};

#define MPLINK_OBJ_SIZE		32
struct mplink mplink_obj[MPLINK_OBJ_SIZE];
struct mplink *mplink_free_obj = NULL;
struct mplink *mplink_using_obj = NULL;
struct msgbody msgobj[MPLINK_OBJ_SIZE];

#define TEST_TASK_NUM	4
pthread_t taskid[TEST_TASK_NUM];


#define TEST_TASK_PRODUCER_NUM		2
#define TEST_TASK_CONSUMER_NUM		2

pthread_t task_prod[TEST_TASK_PRODUCER_NUM];
pthread_t task_cons[TEST_TASK_CONSUMER_NUM];

unsigned char global_alive;


static void dump_mplink(struct mplink *list, const char *str)
{
    struct mplink *p;
    int i;
    tc_log("mplink dump %s", str);
    for (p = list, i = 0; p != NULL; p = p->next, i++) {
        tc_log("[%d] p %p ptr %p %s", i, p, p->ptr, (char *)p->ptr);
    }
}

static int check_ptr(struct msgbody *body)
{
    return !0;
}

static void *thread_prod(void *arg)
{
	int num, size, sleep, i;
	int loop = (int)arg;
	char buff[TEXT_SIZE_MAX];
	struct msgbody *p;

	for (i = 0; i < sizeof(buff); ++i) {
		buff[i] = i;
	}

	while (global_alive) {
		num = rand() % 2 + 1;
		while (num--) {
			size = rand() % TEXT_SIZE_MAX;

			p = mplink_new(&mplink_free_obj, &mplink_using_obj);
		    if (p == NULL) {
		        tc_log_err("new cnt %d failed", i);
		        break;
		    }
		    memset(p->text, 0x0, sizeof(p->text));
		    memcpy(p->text, buff, size);
		    p->size = size;
		}

		if ((--loop) == 0) {
			break;
		}

		sleep = rand() % (10 * 1000);
		tc_log("sleeping %dus", sleep);
		usleep(sleep);
	}
}

static void thread_cons(void *arg)
{
	int num, size, sleep, i, ret;
	int loop = (int)arg;
	char buff[TEXT_SIZE_MAX];
	struct msgbody *p;

	for (i = 0; i < sizeof(buff); ++i) {
		buff[i] = i;
	}

	while (global_alive) {
		num = rand() % 2 + 1;
		while (num--) {

			p = (struct msgbody *)mplink_iterator(mplink_using_obj, NULL, 0, check_ptr);
		    if (p == NULL) {
		        tc_log_err("find node failed when consume");
		        break;
		    }

		    if (memcmp(buff, p->text, p->size)) {
		    	tc_log_err("check text failed size %d", p->size);
		    	// dump memory
		    	hex_dump("err text", p->text, p->size);

		    	// setting alive failed
		    	global_alive = 0;
		    	return ;
		    }

		    ret = mplink_del(&mplink_free_obj, &mplink_using_obj, p);
		    tc_log("mplink_del ret %d", ret);
		}

		if ((--loop) == 0) {
			break;
		}

		sleep = rand() % (10 * 1000);
		tc_log("sleeping %dus", sleep);
		usleep(sleep);
	}
}

static void *test_thread(void *arg)
{
	int tid = (int)arg;
	int num, ret, i, cnt;
	struct msgbody *p, *q;

	num = 2;
	while (num--) {
		cnt = rand() % MPLINK_OBJ_SIZE + 1;
		tc_log("tid %d try to new %d times", tid, cnt);

		for (i = 0; i < cnt; ++i) {
			p = mplink_new(&mplink_free_obj, &mplink_using_obj);
		    if (p == NULL) {
		        tc_log_err("new cnt %d failed", i);
		        break;
		    }
		    snprintf(p->text, sizeof(p->text), "tid %d test %d", tid, i);
		}

	    // dump_mplink(mplink_free_obj, "free");
	    // dump_mplink(mplink_using_obj, "using");

	    cnt = rand() % MPLINK_OBJ_SIZE + 1;
		tc_log("tid %d try to del %d times", tid, cnt);

		for (i = 0; i < cnt; ++i) {
		    q = (struct msgbody *)mplink_iterator(mplink_using_obj, NULL, 0, check_ptr);
		    if (q == NULL) {
		        tc_log_err("del cnt %d failed", i);
		        continue;
		    }

		    ret = mplink_del(&mplink_free_obj, &mplink_using_obj, q);
		    tc_log("mplink_del ret %d", ret);
		}

	    // dump_mplink(mplink_free_obj, "free");
	    // dump_mplink(mplink_using_obj, "using");
	}

	dump_mplink(mplink_free_obj, "free");
	dump_mplink(mplink_using_obj, "using");

	return NULL;
}

/**
 * @brief
 * @param  None
 * @retval None
 */
void mplink_test_run(void)
{
	int i, ret, loop = 10;

	srand(time(NULL));

    mplink_init(mplink_obj, MPLINK_OBJ_SIZE, &mplink_free_obj, &mplink_using_obj, \
    			msgobj, sizeof(struct msgbody));
    dump_mplink(mplink_free_obj, "free");
    dump_mplink(mplink_using_obj, "using");

    global_alive = !0;

    // for (i = 0; i < TEST_TASK_NUM; ++i) {
    // 	ret = pthread_create(&taskid[i], NULL, test_thread, i);
	   //  if (ret != 0) {
	   //      tc_log_err("create thread failed");
	   //      exit(-1);
	   //  }
    // }

    // for (i = 0; i < TEST_TASK_NUM; ++i) {
    // 	pthread_join(taskid[i], NULL);
    // }

    for (i = 0; i < TEST_TASK_PRODUCER_NUM; ++i) {
    	ret = pthread_create(&task_prod[i], NULL, thread_prod, loop);
	    if (ret != 0) {
	        tc_log_err("create thread_prod failed");
	        exit(-1);
	    }
    }

    for (i = 0; i < TEST_TASK_CONSUMER_NUM; ++i) {
    	ret = pthread_create(&task_cons[i], NULL, thread_cons, loop);
	    if (ret != 0) {
	        tc_log_err("create thread_cons failed");
	        exit(-1);
	    }
    }

    // global_alive = 0;

    for (i = 0; i < TEST_TASK_PRODUCER_NUM; ++i) {
    	pthread_join(task_prod[i], NULL);
    }

    for (i = 0; i < TEST_TASK_CONSUMER_NUM; ++i) {
    	pthread_join(task_cons[i], NULL);
    }
}

/* End of file */



